/**
 * table 合计行函数
 */
import XEUtils from 'xe-utils'
import { VxeColumnProps } from 'vxe-table'

import { sum } from './sum'

const IGNORE_ALL_FOOTER_KEY = 'ignoreAllFooter'
const IGNORE_SUM_KEY = 'ignoreSum'

type CalcFooterType = keyof typeof footerTypes

type SumIterate =
  | string
  | number
  | ((options: { field: string }, item: any, index: number, list: any[]) => number)

type ItemOptions = {
  /** 求和类型 count-唯一计数,quantity-总数,sum-求和  */
  type?: CalcFooterType
  /** 对值进行格式化方法,会忽略默认前缀 */
  formatter?: ({ result }) => string
  /** 自定义求和迭代返回函数 */
  sumIterate?: SumIterate
  /** 保留小数位,auto取计算数据最高小数位 */
  decimals?: number | 'auto'
  /** 是否补零,对decimals非auto生效 */
  fillZero?: boolean
  /** 是否显示前缀 */
  isPrefix?: boolean
  /** 是否填充非求和项占位符 */
  placeholder?: string
}

type CalcFooterGlobalOptions = ItemOptions

type GetResultType = ItemOptions & {
  $grid: any
  data: any[]
  column: VxeColumnProps
}

type CalcFooterItem = CalcFooterType | ItemOptions

type CalcFooter = {
  [key in string]: CalcFooterItem
}

const footerTypes = {
  count: {
    prefix: '计数: '
  },
  quantity: {
    prefix: '总数: '
  },
  sum: {
    prefix: '求和: '
  }
}

const defOptions: ItemOptions = {
  decimals: 'auto',
  fillZero: false,
  isPrefix: false,
  placeholder: 'N/A'
}

function getSumIterate(sumIterate: SumIterate | undefined, field: string) {
  if (sumIterate) {
    if (XEUtils.isFunction(sumIterate)) {
      return function (item: any, index: number, list: any[]) {
        return sumIterate({ field }, item, index, list)
      }
    }
    return sumIterate
  }
  return function (item) {
    return item[IGNORE_ALL_FOOTER_KEY] || item[IGNORE_SUM_KEY] ? 0 : item[field]
  }
}

function handleCalc({ type, data, column, $grid, sumIterate, decimals, fillZero }: GetResultType) {
  const field = column.field!
  let result: number = 0
  let group: { [key: string]: number } = {}
  switch (type) {
    case 'count':
    case 'quantity':
      group = XEUtils.countBy(data, (row) => {
        if (field === 'vxe-seq' || column.type === 'seq') {
          return $grid.getRowSeq(row)
        }
        return row[field] || undefined
      })
      Reflect.deleteProperty(group, 'undefined')
      if (type === 'count') {
        result = Object.keys(group).length
        break
      } else if (type === 'quantity') {
        result = Object.values(group).reduce((curr, item) => {
          return curr + item
        }, 0)
        break
      }
      break
    case 'sum':
      result = sum(data, getSumIterate(sumIterate, field))
      break
  }

  // 计数不保留小数
  if (type && ['count', 'quantity'].includes(type)) {
    return result
  }

  if (decimals === 'auto') {
    return result
  } else {
    const decimalsMethod = fillZero ? XEUtils.toFixed : XEUtils.round
    return decimalsMethod(result, decimals)
  }
}

function getResult(
  baseData: { $grid; data; column },
  itemOptions: CalcFooterItem,
  globalOptions?: CalcFooterGlobalOptions
) {
  const options = Object.assign(
    {},
    defOptions,
    globalOptions,
    XEUtils.isObject(itemOptions) ? itemOptions : { type: itemOptions }
  )
  const { formatter, isPrefix, type } = options
  const result = handleCalc({
    ...baseData,
    ...options
  })

  if (formatter) {
    return formatter({ result })
  }
  if (isPrefix && type) {
    return `${footerTypes[type].prefix}${result}`
  }
  return result
}

/** 根据求和field数组转换成合计对象 */
const transformSumFields = (sumFields: string[], type: CalcFooterType = 'sum') => {
  return sumFields.reduce((acc, field: string) => {
    acc[field] = type
    return acc
  }, {})
}

/**
 * 合计行 计数(非空唯一数) 求和(精度为被求和值最高的小数位)
 * @param {CalcFooter} calcItem
 * @returns {string[][]}
 */
const calcFooter = function (calcItems: CalcFooter, options?: CalcFooterGlobalOptions) {
  return function ({ columns, data, $grid }) {
    calcItems = Object.assign({}, { 'vxe-seq': 'count' }, calcItems)
    const keys = Object.keys(calcItems)
    const footerData = [
      columns.map((column) => {
        const field = column.field
        if (keys.includes(field)) {
          const itemOpt = calcItems[field]
          return getResult({ $grid, data, column }, itemOpt, options)
        }
        return options?.placeholder || defOptions.placeholder || null
      })
    ]
    return footerData
  }
}

export type { CalcFooterType, SumIterate, CalcFooterItem, GetResultType, CalcFooter }
export { footerTypes, calcFooter, transformSumFields }

// console.log(XEUtils.toFixed(1.005, 2)); // '1.01'
// console.log(XEUtils.toFixed(1.0, 2)); // '1.00'
// console.log(XEUtils.round(1.005, 2)); // 1.01
// console.log(XEUtils.round(1.0, 2)); // 1
// console.log((1.005).toFixed(2)); // '1.00'

// // 生成10万条数据，每项有100个字段
// const generateData = (numItems, numFields) => {
//   const data = [];
//   for (let i = 0; i < numItems; i++) {
//     const item = {};
//     for (let j = 0; j < numFields; j++) {
//       item[`field${j}`] = Math.random() * 100; // 随机生成0到100之间的数值
//     }
//     data.push(item);
//   }
//   return data;
// };

// // 方案一：遍历字段，每个字段遍历数据
// const sumFieldsV1 = (data, fields) => {
//   return fields.map((field) => {
//     const result = data.reduce((curr, item) => {
//       return curr + item[field];
//     }, 0);
//     return `${field}: ${result}`;
//   });
// };

// // 方案二：遍历数据，每个数据项遍历字段
// const sumFieldsV2 = (data, fields) => {
//   const sum = {};
//   data.forEach((item) => {
//     fields.forEach((field) => {
//       if (!sum[field]) {
//         sum[field] = 0;
//       }
//       sum[field] += item[field];
//     });
//   });
//   return Object.keys(sum).map((field) => `${field}: ${sum[field]}`);
// };

// // 测试配置
// const numItems = 500000; // 10万条数据
// const numFields = 100; // 每项100个字段

// const fieldsToSum = Array.from({ length: 10 }, (_, i) => `field${i}`); // 对前10个字段求和

// // 生成数据
// console.time('生产');
// const data = generateData(numItems, numFields);
// console.timeEnd('生产');

// // 测试方案一
// console.time('方案一');
// const resultV1 = sumFieldsV1(data, fieldsToSum);
// console.timeEnd('方案一');

// // 测试方案二
// console.time('方案二');
// const resultV2 = sumFieldsV2(data, fieldsToSum);
// console.timeEnd('方案二');

// // 输出结果
// console.log('方案一结果:', resultV1);
// console.log('方案二结果:', resultV2);
